/*
 * Copyright 2023 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.mantisrx.runtime.core;

import io.mantisrx.runtime.core.functions.FilterFunction;
import io.mantisrx.runtime.core.functions.FlatMapFunction;
import io.mantisrx.runtime.core.functions.MapFunction;
import io.mantisrx.runtime.core.functions.ReduceFunction;

public interface KeyedMantisStream<K, IN> {

    /**
     * Applies the provided {@link MapFunction} to each element in the stream
     * and returns a new keyed stream consisting of result elements partitioned
     * with existing keys.
     *
     * @param mapFn the function to apply to each element in the stream.
     * @param <OUT> the type of the output elements in the resulting stream.
     * @return a new {@link KeyedMantisStream}
     */
    <OUT> KeyedMantisStream<K, OUT> map(MapFunction<IN, OUT> mapFn);

    /**
     * Applies the provided {@link FlatMapFunction} to each element in the stream
     * and returns a new keyed stream consisting of result elements partitioned
     * with existing keys.
     * Compared to {@link MapFunction}, each element might produce zero, one,
     * or more elements.
     *
     * @param flatMapFn the function to apply to each element in the stream.
     * @param <OUT> the type of the output elements in the resulting stream.
     * @return a new {@link KeyedMantisStream}
     */
    <OUT> KeyedMantisStream<K, OUT> flatMap(FlatMapFunction<IN, OUT> flatMapFn);

    /**
     * Applies the provided {@link FilterFunction} to each element in the stream
     * and returns all elements that match {@code filterFn.apply()}. The function is
     * applied independently to each element in the stream.
     *
     * @param filterFn the function to apply on each element in the stream. Returns boolean
     * @return a new {@link KeyedMantisStream}
     */
    KeyedMantisStream<K, IN> filter(FilterFunction<IN> filterFn);

    /**
     *
     * Defines a windowing operation that partitions the stream into windows of
     * elements, based on the provided {@link WindowSpec}.
     *
     * This method defines a windowing operation that partitions the stream into
     * windows of elements, based on the provided {@link WindowSpec}. The
     * {@link WindowSpec} defines the criteria for creating windows, such as the
     * window size, the sliding interval, or the type of trigger to use.
     * Mantis primarily deals with operational metrics and so usually involves
     * processing times only.
     * This method can only be applied to a {@link KeyedMantisStream}, as the
     * windowing operation requires partitioning the stream by key.
     * Also, the current implementation requires a window function in
     * {@link KeyedMantisStream} that is immediately followed by a
     * {@link KeyedMantisStream#reduce(ReduceFunction)}
     *
     * @param spec the specification of the windows to create.
     * @return a {@link KeyedMantisStream} that applies the windowing operation*
     */
    KeyedMantisStream<K, IN> window(WindowSpec spec);

    /**
     * Defines a reduction operation that aggregates elements in the stream using
     * the provided {@link ReduceFunction}.
     *
     * This method defines a reduction operation that aggregates elements in the
     * stream using the provided {@link ReduceFunction}. The {@link ReduceFunction}
     * defines the logic to combine multiple elements into a single, aggregated
     * result.
     *
     * The method returns a new {@link MantisStream} that contains the results of
     * the reduction operation. The resulting stream contains only the output
     * elements generated by the {@link ReduceFunction}, and not the intermediate
     * values produced during the aggregation.
     *
     * The type of the output elements in the resulting stream is determined by the
     * type parameter of the {@link ReduceFunction}.
     *
     * Note: This function must immediately follow {@link KeyedMantisStream#window(WindowSpec)}
     *   because of current implementation assumptions.
     *
     * @param reduceFn the function that aggregates elements in the stream.
     * @param <OUT> the type of the elements in the output stream.
     * @return a new {@link MantisStream} with result of reduction operation.
     */
    <OUT> MantisStream<OUT> reduce(ReduceFunction<IN, OUT> reduceFn);
}
